
/********************************** LICENSE ************************************
 *
 * Copyright 2011 Alessandro Carrega
 * email: alessandro.carrega@unige.it
 *
 * This file is part of simulation_time.
 *
 * simulation_time is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * simulation_time is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with simulation_time.  If not, see <http://www.gnu.org/licenses/>.
 *
 ********************************* LICENSE ************************************/

#include <cmath>
#include <iostream>
#include <stdio.h>

#include "simulation_time.hpp"

static char *common = new char[100];

/*
 * ATTENZIONE:
 * dato che la minima granularità da considerarsi è 51.2 ns, 
 * la macro NANO è stata portata a 0.1 ns (1e-10 s)
 */

static void round(double& sec, double& nano) 
// arrotondamento nel caso il valore dei nanosecondi superi il secondo
{
	if ( nano > 0 ) {
		while (nano >= NANO) {
			nano -= (double) NANO;
			sec++;
		}
	} else {
		while ( nano <= (- NANO) ) {
			nano += (double) NANO;
			sec--;
		}
	}
}

static void adjust(double& sec, double& nano)
// aggiusto i segni in modo che siano uguali
{
	if ( (sec > 0) && (nano < 0) ) {
		nano += (double) NANO;
		sec--;
	}

	if ( (sec < 0) && (nano > 0) ) {
		nano -= (double) NANO;
		sec++;
	}
	// a questo punto i segni devono coincidere.
}

SimulationTime::SimulationTime()
//costruttore: senza argomenti
{
	build(0,0);
}

SimulationTime::SimulationTime(double sec, double nano = 0)
//costruttore: parte intera + frazionaria (in nanosecondi, opzionale)
{
	build(sec, nano);
}

SimulationTime::SimulationTime(int sec, int nano = 0)
//costruttore: parte intera + frazionaria (in nanosecondi, opzionale)
{
	build(sec,nano);
}

SimulationTime::SimulationTime(double time)
//costruttore, a partire da un tempo espresso con variabile double
{
	bool negative = false;
	if (time < 0) {      // nel caso di numero negativo me lo segno e lo tratto come positivo
		negative = true;
		time = -time;
	}

	double secondi = (double) floor(time);  // secondi va' bene cosi'
	double nanoSecondi = (double) rint( (time - secondi) * NANO );  // questo per maggiore precisione

	if (negative == true) {
		secondi = -secondi;
		nanoSecondi = -nanoSecondi;
	}

#ifdef DEBUG
	cout << "Secondi estratti: " << secondi << " prova: " << (long int) rint( (time - secondi) * NANO ) << endl;
	cout << " nanosecondi estratti: " << nanoSecondi << " prova: " << rint( (time - secondi) * NANO ) << endl;
#endif

	build(secondi, nanoSecondi);
}

SimulationTime::SimulationTime(const SimulationTime& s)
// copy-constructor
{
	build(s.getSecond(), s.getNanoSecond());
}

void SimulationTime::build(double sec, double nano)
// la costruzione dell'oggetto
{
	round(sec, nano);  // prima riporto i valori dei nanosecondi nella loro unita'

	adjust(sec, nano); //adesso controllo i due segni

	second =  sec;
	nanoSecond = nano;
}

double SimulationTime::getSecond() const
// ritorna i secondi del tempo
{
	return(second);
}

double SimulationTime::getNanoSecond() const
// ritorna i nanosecondi del tempo
{
	return(nanoSecond);
}


void operator<<(std::ostream& os, SimulationTime t) // l'operatore per visualizzare il tempo
{
	os << t.second << " secondi " << t.nanoSecond << " nanosecondi";
}

bool operator<(SimulationTime t1, SimulationTime t2) // Operatore di confronto
{
	if((t1.getSecond() < t2.getSecond()) || ((t1.getSecond() == t2.getSecond()) && (t1.getNanoSecond() < t2.getNanoSecond())))
		return true;
	return false;
}

bool operator>(SimulationTime t1, SimulationTime t2) // Operatore di confronto
{
	if((t1.getSecond() > t2.getSecond()) || ((t1.getSecond() == t2.getSecond()) && (t1.getNanoSecond() > t2.getNanoSecond())))
		return true;
	return false;
}

bool operator==(SimulationTime t1, SimulationTime t2) // Operatore di confronto
{
	if( (t1.getSecond() == t2.getSecond()) && (t1.getNanoSecond() == t2.getNanoSecond()) )
		return true;
	return false;
}

bool operator<=(SimulationTime t1, SimulationTime t2) // Operatore di confronto
{
	if ( (t1 < t2) || (t1 == t2) )
		return true;
	return false;
}

bool operator>=(SimulationTime t1, SimulationTime t2) // Operatore di confronto
{
	if ( (t1 > t2) || (t1 == t2) )
		return true;
	return false;
}

SimulationTime operator+(SimulationTime t1, SimulationTime t2) // Operatore artimetico somma
{
	double sec = t1.getSecond() + t2.getSecond();             // somma
	double nano = t1.getNanoSecond() + t2.getNanoSecond();    // algebrica!!
	round(sec, nano);
	adjust(sec, nano);
	SimulationTime t(sec, nano);
	return(t);
}


SimulationTime operator*(SimulationTime t1, double t2) // Operatore artimetico somma
{
	double sec = t1.getSecond() * (double)t2;
	double nano = t1.getNanoSecond() * (double)t2;
	round(sec, nano);
	adjust(sec, nano);
	SimulationTime t(sec, nano);
	return(t);
}

SimulationTime& SimulationTime::operator+=(SimulationTime tempo) // Operatore aritmetico somma
{
	//cout<<"1) sec= "<<sec<<"\t decimi di nano"<<nano<<endl;
	double sec = second + tempo.getSecond();            // somma
	//cout<<"2) sec= "<<sec<<"\t decimi di nano"<<nano<<endl;
	double nano = nanoSecond + tempo.getNanoSecond();   // algebrica!!
	//cout<<"1) sec= "<<sec<<"\t decimi di nano"<<nano<<endl;
	round(sec, nano);                                     // e' necessario arrotondare
	//cout<<"2) sec= "<<sec<<"\t decimi di nano"<<nano<<endl;
	adjust(sec, nano);                                    // e aggiustare i segni
	//cout<<"3) sec= "<<sec<<"\t decimi di nano"<<nano<<endl;
	second =  sec;
	nanoSecond = nano;
	//cout<<"4) sec= "<<sec<<"\t decimi di nano"<<nano<<endl;
	return *this;
}

SimulationTime operator-(SimulationTime t1, SimulationTime t2) // Operazione di sottrazione algebrica
{
	double sec = t1.getSecond() - t2.getSecond();
	double nano = t1.getNanoSecond() - t2.getNanoSecond();  // Somma algebrica anche qui!!
	round(sec, nano);                                         // e' necessario arrotondare
	adjust(sec, nano);                                        // e aggiustare i segni
	SimulationTime t(sec, nano);
	return(t);
}

SimulationTime& SimulationTime::operator-=(SimulationTime tempo) // Operazione di sottrazione algebrica
{
	double sec = second - tempo.getSecond();                // Somma
	double nano = nanoSecond - tempo.getNanoSecond();       // algebrica!!
	round(sec, nano);                                         // e' necessario arrotondare
	adjust(sec, nano);                                        // e aggiustare i segni
	second = sec;
	nanoSecond = nano;
	return *this;
}

SimulationTime::operator double() const // Definizione dal cast a double
{
#ifdef DEBUG
	cout << "SimTime in double: " << (second + nanoSecond/NANO) << endl;
	cout << "+- nanosecondi: " << nanoSecond << endl;
	double y = nanoSecond/NANO;
	cout << "+- in double: " << y << endl;
	double x = second + y;
	cout << "+- totale: " << x << endl;
#endif

	return ((second + nanoSecond/NANO));
}

char *SimulationTime::toString() const // ritorna una stringa con il valore del tempo
{
	char *stringa = common;
	//      char *stringa = new char[100];
	sprintf(stringa, "%e secondi %e nanosecondi",(double)second, (double)nanoSecond);
	//      if (second == 13)
	//      exit(0);
	return(stringa);
}  
